#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_eap_client.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"
#include "esp_mac.h"
#include "driver/gpio.h"

#define BOOT_MODE_PIN 9

/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t s_wifi_event_group;
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static const char *TAG = "smartconfig_example";

static void smartconfig_example_task(void *parm);

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT)
    {
        switch (event_id)
        {
        case WIFI_EVENT_STA_START:
        {
            wifi_config_t cfg = {0};
            esp_wifi_get_config(ESP_IF_WIFI_STA, &cfg);
            if (strlen((char *)cfg.sta.ssid) > 0)
            {
                ESP_LOGI(TAG, "already set ,SSID is: %s,start connect", cfg.sta.ssid);
                esp_wifi_connect();
            }
            else
            {
                ESP_LOGI(TAG, "not set, start to config");
                // Handle Wi-Fi station start event
                xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
            }
        }
        break;

        case WIFI_EVENT_STA_DISCONNECTED:
            // Handle Wi-Fi station disconnected event
            esp_wifi_connect();
            xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
            break;

        default:
            // Handle other Wi-Fi events if needed
            break;
        }
    }
    else if (event_base == IP_EVENT)
    {
        if (event_id == IP_EVENT_STA_GOT_IP)
        {
            // Handle got IP event
            xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
        }
    }
    else if (event_base == SC_EVENT)
    {
        switch (event_id)
        {
        case SC_EVENT_SCAN_DONE:
            // Handle Smart Config scan done event
            ESP_LOGI(TAG, "Scan done");
            break;

        case SC_EVENT_FOUND_CHANNEL:
            // Handle Smart Config found channel event
            ESP_LOGI(TAG, "Found channel");
            break;

        case SC_EVENT_GOT_SSID_PSWD:
            // Handle Smart Config got SSID and password event
            ESP_LOGI(TAG, "Got SSID and password");
            smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
            wifi_config_t wifi_config;
            uint8_t ssid[33] = {0};
            uint8_t password[65] = {0};
            uint8_t rvd_data[33] = {0};

            bzero(&wifi_config, sizeof(wifi_config_t));
            memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
            memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));

            memcpy(ssid, evt->ssid, sizeof(evt->ssid));
            memcpy(password, evt->password, sizeof(evt->password));
            ESP_LOGI(TAG, "SSID:%s", ssid);
            ESP_LOGI(TAG, "PASSWORD:%s", password);
            if (evt->type == SC_TYPE_ESPTOUCH_V2)
            {
                ESP_ERROR_CHECK(esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)));
                ESP_LOGI(TAG, "RVD_DATA");
                for (int i = 0; i < 33; i++)
                {
                    printf("%02x ", rvd_data[i]);
                }
                printf("\n");
            }
            ESP_ERROR_CHECK(esp_wifi_disconnect());
            ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
            esp_wifi_connect();

            break;

        case SC_EVENT_SEND_ACK_DONE:
            // Handle Smart Config send ACK done event
            xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
            break;

        default:
            // Handle other Smart Config events if needed
            break;
        }
    }
}

static void initialise_wifi(void)
{
    // Initialize the TCP/IP stack
    ESP_ERROR_CHECK(esp_netif_init());

    // Create an event group to manage Wi-Fi events
    s_wifi_event_group = xEventGroupCreate();

    // Create the default event loop for handling system events
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // Create a default Wi-Fi station (STA) network interface
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif); // Ensure the network interface is created successfully

    // Initialize Wi-Fi with default configuration
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // Register event handlers for Wi-Fi events, IP events, and Smart Config events
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));

    // Set Wi-Fi mode to station (STA) mode
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

    // Start the Wi-Fi driver
    ESP_ERROR_CHECK(esp_wifi_start());
}
static void smartconfig_example_task(void *parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK(esp_smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS));
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_smartconfig_start(&cfg));

    while (1)
    {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if (uxBits & CONNECTED_BIT)
        {
            ESP_LOGI(TAG, "Wifi Connected to ap");
        }
        else if (uxBits & ESPTOUCH_DONE_BIT)
        {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(NULL);
        }
    }
}
void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());

    initialise_wifi();

    gpio_reset_pin(BOOT_MODE_PIN);
    gpio_set_direction(BOOT_MODE_PIN, GPIO_MODE_INPUT);

    while (1)
    {
        if (gpio_get_level(BOOT_MODE_PIN) == 0)
        {
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            if (gpio_get_level(BOOT_MODE_PIN) == 0)
            {
                ESP_LOGI(TAG, "restore all wifi set ......");
                esp_wifi_restore();
            }
        }
        vTaskDelay(200 / portTICK_PERIOD_MS);
    }
}
